﻿/**
 * IMonAIR: A new open-source library for instant messenger connectivity. <http://code.google.com/p/imonair/>
 *
 * This software is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
 */
/**
 * @author Andre de Groot <http://www.cmd.tech.nhl.nl/users/groot406/weblog/>
 * @author Piotr Zema <http://felixz.mark-naegeli.com>
 */
/***************************************\
 * Known Bugs:
 *	- 	Problem: 			Sometimes when the contact list is loaded, we don't get a complete response.
 *		Situation:			Unknown
 *		Solution:			No solution yet
 *		Current workaround:	When the first ILN message is received, 
 *							a ContactListSynced event will also  be dispatched (if not done before)
 * TODO Find Solution for socket security problem.
 * TODO Find Solution for the SSL Connection problem.
 * TODO Block actions, untill connected successfully.
 * TODO Restore Groups conversations.
 ***************************************/
package net.imonair.msn.connectors {
	import adobe.crypto.MD5;
	import flash.events.EventDispatcher;
	import flash.events.ProgressEvent;
	import flash.events.IOErrorEvent;
	import flash.events.TimerEvent;
	import flash.events.Event;
	import flash.text.TextFormat;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	import flash.net.URLRequest;
	import flash.net.URLLoader;
	import flash.net.Socket;
	import flash.utils.Dictionary;
	import net.imonair.core.connectors.IIMConnector;
	import net.imonair.core.contacts.IMContactList;
	import net.imonair.core.contacts.IMContactList;
	import net.imonair.core.contacts.IIMContact;
	import net.imonair.core.events.IMContactListEvent;
	import net.imonair.msn.connectors.MSNContactListType;
	import net.imonair.msn.contacts.MSNContact;
	import net.imonair.msn.contacts.MSNStatus;
	import net.imonair.msn.events.MSNContactListEvent;
	import net.imonair.msn.events.MSNConnectorEvent;
	import net.imonair.msn.events.MSNContactEvent;
	import net.imonair.msn.events.MSNChatEvent;
	import net.imonair.msn.chats.MSNSwitchBoard;
	import net.imonair.msn.chats.MSNMessage;
	import flash.utils.Timer;
	/**
	 * Dispatched when a new connection is initialized.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CONNECT
	 */
    [Event(name = "connect", type = "net.imonair.msn.events.MSNConnectorEvent")]
	/**
	 * Dispatched when network is unavailable.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CONNECT_FAIL
	 */
    [Event(name = "connect failed", type = "net.imonair.msn.events.MSNConnectorEvent")]
	/**
	 * Dispatched when connection is terminated.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.DISCONNECT
	 */
    [Event(name = "disconnect", type = "net.imonair.msn.events.MSNConnectorEvent")]
	/**
	 * Dispatched when user signs in successfully.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.SIGN_IN
	 */
    [Event(name = "sign in", type = "net.imonair.msn.events.MSNConnectorEvent")]
	/**
	 * Dispatched when user's status changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.STATUS_CHANGE
	 */
    [Event(name = "status change", type = "net.imonair.msn.events.MSNConnectorEvent")]
	/**
	 * Dispatched when user's display name changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.NAME_CHANGE
	 */
    [Event(name = "name change", type = "net.imonair.msn.events.MSNConnectorEvent")]
	/**
	 * Dispatched when when contact goes online.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CONTACT_ONLINE
	 */
    [Event(name = "contact online", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when contact goes offline.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CONTACT_OFFLINE
	 */
    [Event(name = "contact offline", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when contact's status changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.STATUS_CHANGE
	 */
    [Event(name = "status change", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when contact's group changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.GROUP_CHANGE
	 */
    [Event(name = "group change", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * Dispatched when any property of contact changes.
	 * @eventType net.imonair.msn.events.MSNConnectorEvent.CHANGE
	 */
    [Event(name = "change", type = "net.imonair.msn.events.MSNContactEvent")]
	/**
	 * This class handles the communication with the MSN protocol.
	 * @playerversion Flash 9
	 * @langversion 3.0
	 * @keyword msn, connector
	 * @see net.imonair.msn.events.MSNConnectorEvent
	 * @see net.imonair.msn.chats.MSNSwitchBoard
	 */ 
	public class MSNConnector extends EventDispatcher implements IIMConnector {
		private static const MESSENGER_NOTIFICATION_SERVER_PORT:uint = 1863;
		private static const MESSENGER_NOTIFICATION_SERVER:String = "messenger.hotmail.com";
		private static const LOCAL_MSN_SERVER_IMMITATOR_PORT:uint = 9998;
		private static const LOCAL_MSN_SERVER_IMMITATOR:String = "localhost";
		private static const LIVE_ID_LOGIN_URL:String = "http://imonair.mozzly.com/getMSNLogin.php";
		private static const USE_LOCAL_SERVER:Boolean = false;
		/**
		 * Determines output of debug information.
		 * @default false
		 */
		public var debug:Boolean = false;
		private var _contactList	:IMContactList;//All user's contacts
		private var forwardList		:IMContactList;//Contacts of who an user receives notifications
		private var allowList		:IMContactList;//Contacts that are allowed to receive user's notifications
		private var blockList		:IMContactList;//Contacts that not Blocked to receive user's notifications
		private var reverseList		:IMContactList;//Contacts that (want to) receive user's notifications
		private var _pendingList	:IMContactList;//Contacts witch user haven't allowed or blocked
		private var userLiveID:String, userPassword:String;
		private var _signedIn:Boolean = false, contactListSynced:Boolean = false;
		private var _trID:uint = 1, numOfContacts:uint, numOfContactGroups:uint;
		private var _groupSessions:Dictionary = new Dictionary();
		private var contactGroups:Array = [];
		private var userContact:MSNContact;
		private var keepAliveTimer:Timer;
		private var socket:Socket;
		/**
		 * Creates new MSNConnector instance. You can also pass login data to connect automatically.
         * @param userLiveID The user's LiveID.
         * @param userPassword The password for user's LiveID.
		 */
		public function MSNConnector(userLiveID:String = null, userPassword:String = null) {
			if (USE_LOCAL_SERVER) {
				setupConnection(LOCAL_MSN_SERVER_IMMITATOR, LOCAL_MSN_SERVER_IMMITATOR_PORT);
				this.keepAliveTimer = new Timer(10);
				this.keepAliveTimer.addEventListener(TimerEvent.TIMER, keepAliveListener);
			} else setupConnection(MESSENGER_NOTIFICATION_SERVER, MESSENGER_NOTIFICATION_SERVER_PORT);
			if (userLiveID && userPassword) signIn(userLiveID, userPassword);
		}
		/**
		 * Provides the information wether user is signed in or not. 
		 */
		public function get signedIn():Boolean {
			return this._signedIn;
		}
        /**
         * Determines the status of the user.
         */
		public function get status():String {
			return this.userContact.status;
		}
		public function set status(value:String):void {
			this.userContact.status = value;
			sendCommand("CHG " + this._trID + " " + value + " " + this.userContact.msnObject.toString() + " \r\n");
		}
        /**
         * Determines the display name of the user.
         */
		public function get displayName():String {
			return this.userContact.displayName;
		}
		public function set displayName(value:String):void {
			value = value.substr(0, 129);
			this.userContact.displayName = value;
			sendCommand("REA " + this._trID + " " + this.userContact.id + " " + escape(value) + "\r\n");
		}
		/**
		 * Provides the current contact list. 
		 */
		public function get contactList():IMContactList {
			return this._contactList;
		}
		/**
		 * Provides the current pending list, used in MSN protocol. 
		 */
		public function get pendingList():IMContactList {//Returns the pending List
			return this._pendingList;
		}
		/**
		 * Provides a list of all online contacts. 
		 */
		public function get onlineContactList():IMContactList {
			var onlineContacts:IMContactList = new IMContactList(this);
			var contacts:Array = this.contactList.contacts;
			for each (var contact:MSNContact in this.contactList.contacts) if (contact.status != MSNStatus.OFFLINE) onlineContacts.addContact(contact);
			return onlineContacts;
		}
/*		public function get groupSessions():Dictionary {//Returns a list of all groupSessions
			return this._groupSessions;
		}
*/		/**
		 * @private
		 */
		public function get trID():uint	{
			return this._trID;
		}
        /**
         * Connects to server.
         * @param userLiveID The user's LiveID.
         * @param userPassword The password for user's LiveID.
         */
		public function signIn(userLiveID:String, userPassword:String):void {
			if (this.userContact && this.userContact.id == userLiveID) return;
			this.userContact = new MSNContact(this);
			this.userContact.id = userLiveID;
			this.userPassword = userPassword;
			if (!this._signedIn && this.socket.connected) sendCommand("VER " + this._trID + " MSNP9 CVR0\r\n");
		}
		/**
		 * Disconnects from server.
		 */
		public function signOut():void {//TODO Write implementation of signOut() function
			
		}
        /**
         * Sends a message to a contact.
         * @param contact The receiver contact.
         * @param message The message to be sent.
         * @param textFormat The textFormat of the message; not implemented yet.
         */
		public function sendMessage(contact:IIMContact, message:String, textFormat:TextFormat = null):void {
			contact.sendMessage(new MSNMessage(message, textFormat));
		}
        /**
         * Returns a contact by providing an id. Should be replaced.
         * @param id The contact's id.
		 * @return A MSN contact.
		 */
		public function getContact(id:String):IIMContact {
			return this.contactList.getContactById(id);
		}
        /**
         * Adds a contact to user's forwardList.
         * @param id The contact's id.
		 */
		public function addContact(id:String, groupID:uint = 0):void {
			var contact:MSNContact = getContact(id) as MSNContact;
			if (contact) {
				if(!contact.isOnList(MSNContactListType.FORWARD)) sendCommand("ADD " + this._trID + " FL " + id + " " + id + " " + groupID + "\r\n");
				if(!contact.isOnList(MSNContactListType.BLOCK) && !contact.isOnList(MSNContactListType.ALLOW) ) sendCommand("ADD " + this._trID + " AL " + id + " " + id + "\r\n");
			} else {
				sendCommand("ADD " + this._trID + " FL " + id + " " + id + " " + groupID + "\r\n");
				sendCommand("ADD " + this._trID + " AL " + id + " " + id + "\r\n");
			}
		}
        /**
         * Removes a contact from user's forwardList.
         * @param id The contact's id.
         * @param block To also block a contact, set this paramewter to true.
		 */
		public function removeContact(id:String, block:Boolean = false):void {
			sendCommand("REM " + this._trID + " FL " + id + "\r\n");
			if (block) blockContact(id);
		}
        /**
         * Blocks a contact.
         * @param id The contact's id.
		 */
		public function blockContact(id:String):void {
			sendCommand("ADD " + this._trID + " BL " + id + " " + id + "\r\n");
			disAllowContact(id);
		}
        /**
         * Unblocks a contact.
         * @param id The contact's id.
		 */
		public function unBlockContact(id:String):void {
			sendCommand("REM " + this._trID + " BL " + id + "\r\n");
			allowContact(id);
		}
        /**
         * Adds a contact to user's allowList.
         * @param id The contact's id.
		 */
		public function allowContact(id:String):void {
			sendCommand("ADD " + this._trID + " AL " + id + " " + id + "\r\n");
		}
        /**
         * Removes a contact from user's allowList.
         * @param id The contact's id.
		 */
		public function disAllowContact(id:String):void {
			sendCommand("REM " + this._trID + " AL " + id + "\r\n");
		}
		/**
		 * Synchronizes contact list with server.
		 */
		public function syncContactList():void {
			sendCommand("SYN " + this._trID + " 0\r\n");
		}
		/**
		 * @private
		 */
		public function sendCommand(command:String):void {
			this.socket.writeUTFBytes(command);
			this.socket.flush();
			if (command != "KAL") {
				this._trID++;
				debugFunction(">>> " + command.substr(0,-2));
			}
		}
		private function set keepAlive(value:Boolean):void {
			if (USE_LOCAL_SERVER) if (value) this.keepAliveTimer.start() else this.keepAliveTimer.stop();
		}
		private function setupConnection (host:String, port:uint):void {
			if (this.socket) {
				this.socket.removeEventListener(Event.CONNECT, connectHandler);
				this.socket.removeEventListener(Event.CLOSE, closeHandler);
				this.socket.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
				this.socket.removeEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
			}
			this.socket = new Socket(host, port);
			this.socket.addEventListener(Event.CONNECT, connectHandler);
			this.socket.addEventListener(Event.CLOSE, closeHandler);
			this.socket.addEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			this.socket.addEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
		}
		private function changeConnection(host:String, port:uint):void	{
			if (USE_LOCAL_SERVER) sendCommand("CON " + host + ":" + port + "\r\n")	else setupConnection(host, port);
		}
		private function keepAliveListener(event:TimerEvent):void {
			sendCommand("KAL");
		}
		private function connectHandler(event:Event):void {
			debugFunction(USE_LOCAL_SERVER ? "Connected to Local Socket" : "Socket connected");
			if (USE_LOCAL_SERVER) sendCommand("CON " + MESSENGER_NOTIFICATION_SERVER + ":" + MESSENGER_NOTIFICATION_SERVER_PORT + "\r\n") else {
				if (this.userContact) sendCommand("VER " + this._trID + " MSNP9 CVR0\r\n");
				dispatchEvent(new MSNConnectorEvent(MSNConnectorEvent.CONNECT));
			}
		}
		private function closeHandler(event:Event):void	{
			this._signedIn = false;
			this.socket.removeEventListener(Event.CONNECT, connectHandler);
			this.socket.removeEventListener(Event.CLOSE, closeHandler);
			this.socket.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			this.socket.removeEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
			this.socket = null;
			dispatchEvent(new MSNConnectorEvent(MSNConnectorEvent.DISCONNECT));
			debugFunction("Connection closed");
		}
		private function IOErrorHandler(event:IOErrorEvent):void {
			this.socket.removeEventListener(Event.CONNECT, connectHandler);
			this.socket.removeEventListener(Event.CLOSE, closeHandler);
			this.socket.removeEventListener(IOErrorEvent.IO_ERROR, IOErrorHandler);
			this.socket.removeEventListener(ProgressEvent.SOCKET_DATA, dataHandler);
			this.socket = null;
			dispatchEvent(new MSNConnectorEvent(MSNConnectorEvent.CONNECT_FAIL));
			debugFunction("Connection failed");
		}
		private function dataHandler(event:ProgressEvent):void {//TODO add AssociatedData handling
			var response:String = this.socket.readUTFBytes(socket.bytesAvailable);
			var responses:Array = response.split("\r\n");
			debugFunction("<<< " + responses.join("\n	"));
			for each (var value:String in responses) {
				var code:String = value.substr(0, 3);
				if (code) if ((code + " ") == value.substr(0, 4).toUpperCase()) try {
					this["handleDataCode_" + code](value);
				} catch (error:ReferenceError) {
					debugFunction("Unhandled code: " + value);
				} else trace("AssociatedData: " + value);
			}
		}
		private function handleDataCode_CON(value:String):void {//The local server connected succesfully to the given server
			debugFunction("Socket connected");
			dispatchEvent(new MSNConnectorEvent(MSNConnectorEvent.CONNECT));
			if (this.userContact) ("VER " + this._trID + " MSNP9 CVR0\r\n");
		}
		private function handleDataCode_VER(value:String):void {//Send the server information about the client
			sendCommand("CVR " + this._trID + " 0x0409 win 4.10 i386 MSNMSGR 7.0.0820 MSMSGS " + this.userContact.id + "\r\n");
		}
		private function handleDataCode_CVR(value:String):void {//Login to the server with the user's LiveID
			sendCommand("USR " + this._trID + " TWN I " + this.userContact.id + "\r\n");
		}
		private function handleDataCode_XFR(value:String):void {//Redirect information
			var data:Array = value.split(" ");
			var host:Array = data[3].split(":");
			switch (data[2]) {
				case "NS"://Notification server redirect
					changeConnection(host[0], host[1]);
					break;
				case "SB"://Connect to switchBoard
					var switchBoard:MSNSwitchBoard = new MSNSwitchBoard(this, host[0], host[1], this.userContact.id, "", data[5], "CALL", data[1], this.debug);
					break;
				default:
					debugFunction("Unknown XFR command: " + value);
			}
		}
		private function handleDataCode_USR(value:String):void {//TODO Authenticate via SSL
			var USRData:Array = value.split(" ");
			if(!this._signedIn)	{
				/**
				 * Authenticate via SSL
				 * Done by external php script on the webserver
				 * Note: cURL has te be installed as a php plugin on the webserver
				 */
				var USR_auth_string	:String 	= USRData[4];
				
				var USR_urlVars:URLVariables 	= new URLVariables();
				USR_urlVars.passport 			= this.userContact.id;
				USR_urlVars.password 			= this.userPassword;
				USR_urlVars.auth_string 		= USR_auth_string.substr(0,-2);
				
				var USR_auth_request:URLRequest	= new URLRequest(LIVE_ID_LOGIN_URL);
				USR_auth_request.data 			= USR_urlVars;
				USR_auth_request.method 		= URLRequestMethod.GET;
				USR_auth_request.contentType 	= URLLoaderDataFormat.TEXT;
				var USR_loader:URLLoader = new URLLoader(USR_auth_request);
				
				USR_loader.addEventListener(Event.COMPLETE, msnLoginResult);
			} else {
				debugFunction("*SIGNED IN*");
				this.userContact.displayName = unescape(USRData[4]);
				dispatchEvent(new MSNConnectorEvent(MSNConnectorEvent.SIGN_IN));
				keepAlive = true;
			}
		}
		private function handleDataCode_SYN(value:String):void {//Synchronisation info
			var data:Array 			= value.split(" ");
			this.numOfContacts		= data[3];
			this.numOfContactGroups	= data[4];
			this._contactList	= new IMContactList(this);
			this.forwardList	= new IMContactList(this);
			this.allowList		= new IMContactList(this);
			this.blockList		= new IMContactList(this);
			this.reverseList	= new IMContactList(this);
			this._pendingList	= new IMContactList(this);
			//Dispatch synced event when there are no contacts
			if(this.numOfContacts == 0)	{
				this.contactListSynced = true;
				dispatchEvent(new IMContactListEvent(IMContactListEvent.SYNCED, this.contactList));
			}
		}
		private function handleDataCode_LSG(value:String):void {//TODO Group information
			var LSGData:Array = value.split(" ");
			var LSG_group:Object = new Object();
			LSG_group.name = LSGData[2];
			LSG_group.id = this.contactGroups.length;
			this.contactGroups.push(LSG_group);
			if(this.contactGroups.length == this.numOfContactGroups) dispatchEvent(new IMContactListEvent(IMContactListEvent.GROUPS_READY, null));
		}
		private function handleDataCode_LST(value:String):void {//Contact information
			var data:Array = value.split(" ");
			var contact:MSNContact = new MSNContact(this);
			contact.id = data[1]
			contact.displayName = data[2];
			contact.listsData = uint(data[3]);
			contact.contactGroups = data[4] as Array
			this.contactList.addContact(contact);
			if (contact.isOnList(MSNContactListType.FORWARD)) this.forwardList.addContact(contact);	
			if (contact.isOnList(MSNContactListType.ALLOW)) this.allowList.addContact(contact);
			if (contact.isOnList(MSNContactListType.BLOCK)) this.blockList.addContact(contact);
			if (contact.isOnList(MSNContactListType.REVERSE)) this.reverseList.addContact(contact);
			if (contact.isOnList(MSNContactListType.REVERSE) && !contact.isOnList(MSNContactListType.ALLOW) && !contact.isOnList(MSNContactListType.BLOCK)) {//Contact is pending
				this._pendingList.addContact(contact);
				dispatchEvent(new MSNContactEvent(MSNContactEvent.ADDED_BY_CONTACT, contact));
			}
			
			if (this.contactList.length == this.numOfContacts) {
				this.contactListSynced = true;
				dispatchEvent(new MSNContactListEvent(MSNContactListEvent.SYNCED, this.contactList));
			}
		}
		private function handleDataCode_ILN(value:String):void {//Already Online Contacts
			//If syncDone Event isn't dispatched yet, dispatch now
			if(!this.contactListSynced)	{
				this.contactListSynced = true;
				dispatchEvent(new IMContactListEvent(IMContactListEvent.SYNCED, this.contactList));
			}
			var data:Array = value.split(" ");
			var contact:MSNContact = getContact(data[3]) as MSNContact || new MSNContact(this);
			contact.status = data[2];
			contact.id = data[3];
			contact.displayName = data[4];
			contact.clientID = data[5];
			contact.msnObject = new XML(data[6]);
			dispatchEvent(new MSNContactEvent(MSNContactEvent.ONLINE, contact));
		}
		private function handleDataCode_NLN(value:String):void {//Status Update from contact
			var data:Array = value.split(" ");
			var contact:MSNContact = getContact(data[2]) as MSNContact || new MSNContact(this);
			contact.status = data[1];
			contact.id = data[2];
			contact.displayName = data[3];
			contact.clientID = data[4];
		}
		private function handleDataCode_FLN(value:String):void {//Contact goes Offline
			var data:Array = value.split(" ");
			var contact:MSNContact = getContact(data[1]) as MSNContact;
			contact.status = MSNStatus.OFFLINE;
			contact.closeSwitchBoard(true);
		}
		private function handleDataCode_RNG(value:String):void {//TODO Receiving a invitation to join a chatSession with a client
			var RNGData:Array = value.split(" ");
			var RNG_sID:String = RNGData[1];
			var RNG_sbHost:Array = RNGData[2].split(":");
			var RNG_ticket:String = RNGData[4];
			var RNG_sb:MSNSwitchBoard = new MSNSwitchBoard(this, RNG_sbHost[0], RNG_sbHost[1], this.userContact.id, RNG_sID, RNG_ticket, "ANSWER", this._trID, this.debug);
		}
		private function handleDataCode_ADD(value:String):void {
			var data:Array = value.split(" ");
			var contact:MSNContact;
			if (data[2] == MSNContactListType.REVERSE) {
				contact = new MSNContact(this);
				contact.id = data[4];
				contact.displayName = data[5];
				this._pendingList.addContact(contact);
				dispatchEvent(new MSNContactEvent(MSNContactEvent.ADDED_BY_CONTACT, contact));
			} else if(!getContact(data[4])) {//New contact on one of user's contactLists
				contact = new MSNContact(this);
				contact.id = data[4];
				contact.displayName = data[5];
				this.contactList.addContact(contact);
			}
		}
		private function handleDataCode_CHL(value:String):void {//Server sends an challange witch user have to respond on, if the response is wrong, user will be disconnected
			keepAlive = false;
			sendCommand("QRY " + this._trID + " msmsgs@msnmsgr.com 32\r\n" + MD5.hash(value.split(" ")[2] + "Q1P7W2E4J9R8U3S5"));
		}
		private function handleDataCode_QRY(value:String):void {
			keepAlive = false;
			debugFunction("Challenge accepted");
		}
		private function msnLoginResult(event:Event):void {//Received the autentication token form LIVE_ID_LOGIN_URL
			event.target.removeEventListener(Event.COMPLETE, msnLoginResult);
			this._signedIn = true;
			sendCommand("USR " + this._trID + " TWN S " + event.target.data + "\r\n");
		}
/*		private function groupConversationStartedHandler(event:MSNChatEvent):void {//Handles when a group session is started
			this._groupSessions[event.data] = event.data;
			forwardEvent(event);
		}
		private function groupConversationClosedHandler(event:MSNChatEvent):void {//Handles when a group session is closed
			delete this._groupSessions[event.data];
			forwardEvent(event);
		}
*/		private function debugFunction(value:String):void {
			if (this.debug) trace(value);
		}
	}
}